library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity twoWireMaster is
  Port(FPGA_Clk : in std_logic;
       I2C_DataToBus : out std_logic;
       I2C_DataFromBus : in std_logic;
       I2C_DataDir : out std_logic;
       I2C_ClkToBus : out std_logic;
       I2C_ClkFromBus : in std_logic;
       I2C_ClkDir : out std_logic;
       Sync : in std_logic);
end twoWireMaster;

architecture beh of twoWireMaster is

  component Clk_200kHz is
	  Port(FPGA_Clk : IN std_logic;
		     I2C_Clk : out std_logic;
		     I2C_Clk_X2 : out std_logic;
		     I2C_Clk_X4 : out std_logic);
  end component Clk_200kHz;
    
  signal sI2C_Clk,sI2C_Clk_X2,sI2C_Clk_X4,sI2C_DataToBus : std_logic;
  signal sreg : std_logic_vector(7 downto 0);
  signal cnt : std_logic_vector(3 downto 0);
  
  type states is (idle,address,ack1,dataHigh,ack2,dataLow,ack3,recieveHigh,recieveLow,stop);
  signal cs : states:=idle;

begin
  
  CLK_Source : Clk_200kHz port map(FPGA_Clk,sI2C_Clk,sI2C_Clk_X2);
  I2C_ClkToBus<=sI2C_Clk;
  I2C_DataToBus<=sI2C_DataToBus;
  
  --Control
  process(sI2C_Clk_X2)
  begin
    if rising_edge(sI2C_Clk_X2) then
      case cs is
        when idle =>
          if sI2C_DataToBus = '0' and sI2C_Clk = '0' then
            cs<=address;
          end if;
        when stop =>
          null;
        when address =>
          if cnt = X"7" then
            cs<=ack1;
          end if;
        when dataHigh =>
          null;
        when dataLow =>
          null;
        when recieveHigh =>
          null;
        when recieveLow =>
          null;
        when ack1 =>
          null;
        when ack2 =>
          null;
        when ack3 =>
          null;
      end case;
    end if;
  end process;
  
  --Datapath I2C_Clk=>out
  process(sI2C_Clk_X2)
  begin
    if rising_edge(sI2C_Clk_X2) then
      case cs is
        when idle =>
          if sI2C_DataToBus = '0' and sI2C_Clk = '0' then
            I2C_ClkDir<='0';
          else
            I2C_ClkDir<='1';
          end if;
        when stop =>
          I2C_ClkDir<='1';
        when others =>
          I2C_ClkDir<='0';
      end case;
    end if;
  end process;
  
  process(sI2C_Clk_X2)
  begin
    if rising_edge(sI2C_Clk_X2) then
      case cs is
        when idle =>
          if Sync ='1' then
            I2C_DataDir<='0';
          else
            I2C_DataDir<='1';
          end if;
        when recieveHigh =>
          I2C_DataDir<='1';
        when recieveLow =>
          I2C_DataDir<='1';
        when ack1 =>
          I2C_DataDir<='1';
        when ack2 =>
          I2C_DataDir<='1';
        when ack3 =>
          I2C_DataDir<='1';
        when others =>
          I2C_DataDir<='0';
      end case;
    end if;
  end process;

  --Datapath: I2C_Data /falling edge
  process(sI2C_Clk_X2)
  begin
    if rising_edge(sI2C_Clk_X2) then
      case cs is
        when idle =>
          if Sync = '1' and sI2C_Clk = '1' then
            sI2C_DataToBus<='0';
          end if;
          cnt<=X"0";
          sreg<="00011010";
        when stop =>
          null;
        when address =>
          if sI2C_Clk = '0' then
            sI2C_DataToBus<=sreg(conv_integer(cnt(2 downto 0)));
          else
            cnt<=cnt+1;
          end if;
        when dataHigh =>
          null;
        when dataLow =>
          null;
        when recieveHigh =>
          null;
        when recieveLow =>
          null;
        when ack1 =>
          null;
        when ack2 =>
          null;
        when ack3 =>
          null;
      end case;
    end if;
  end process;
  
end beh;